package com.choosefine.paycenter.pay.mq;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.aliyun.openservices.ons.api.Message;
import com.aliyun.openservices.ons.api.transaction.LocalTransactionChecker;
import com.aliyun.openservices.ons.api.transaction.TransactionStatus;
import com.choosefine.paycenter.common.enums.PayStatus;
import com.choosefine.paycenter.common.enums.RechargeStatus;
import com.choosefine.paycenter.common.utils.SerialNumberUtils;
import com.choosefine.paycenter.pay.api.RechargeService;
import com.choosefine.paycenter.pay.constants.PayConstants;
import com.choosefine.paycenter.pay.service.PaymentService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * Comments：
 * Author：Jay Chang
 * Create Date：2017/5/2
 * Modified By：
 * Modified Date：
 * Why & What is modified：
 * Version：v1.0
 */
@Slf4j
@Component
public class PayLocalTransactionCheckerImpl implements LocalTransactionChecker {

    @Autowired
    private PaymentService paymentService;

    @Autowired
    private RechargeService rechargeService;

    @Autowired
    private SerialNumberUtils serialNumberUtils;

    @Override
    public TransactionStatus check(Message msg) {
        //消息ID(有可能消息体一样，但消息ID不一样, 当前消息属于Half 消息，所以消息ID在控制台无法查询)
        final String msgId = msg.getMsgID();
        final String tag = msg.getTag();
        //消息体内容进行crc32, 也可以使用其它的方法如MD5
        //long crc32Id = HashUtil.crc32Code(msg.getBody());
        //消息ID、消息体crc32Id主要是用来防止消息重复
        //如果业务本身是幂等的, 可以忽略, 否则需要利用msgId或crc32Id来做幂等
        //如果要求消息绝对不重复, 推荐做法是对消息体使用crc32或md5来防止重复消息.
        //业务自己的参数对象, 这里只是一个示例, 实际需要用户根据情况来处理
        final String jsonStr = new String(msg.getBody());
        JSONObject payOrderMessage = JSON.parseObject(jsonStr);
        TransactionStatus transactionStatus = TransactionStatus.Unknow;
        try {
            //这里isCommit是检查本地事务有没有执行成功的意思
            boolean isCommit = false;
            final String sn = payOrderMessage.getString("sn");
            if (PayConstants.PAY_TAG_PAY_SUCCESS.equals(tag)) {
                if(serialNumberUtils.isPaySn(sn)) {
                    PayStatus payStatus = paymentService.findStatusByPaySn(sn);
                    if (null != payStatus && PayStatus.PAID.equals(payStatus)) {
                        isCommit = true;
                    }
                }else if(serialNumberUtils.isRechargeSn(sn)){
                    RechargeStatus rechargeStatus = rechargeService.findStatusByRechargeSn(sn);
                    if(null != rechargeStatus && RechargeStatus.COMPLETE.equals(rechargeStatus)){
                        isCommit = true;
                    }
                }
            }else if(PayConstants.PAY_TAG_PAY_FAILURE.equals(tag)){
                if(serialNumberUtils.isPaySn(sn)) {
                    PayStatus payStatus = paymentService.findStatusByPaySn(sn);
                    if (null != payStatus && PayStatus.FAILURE.equals(payStatus)) {
                        isCommit = true;
                    }
                }else if(serialNumberUtils.isRechargeSn(sn)){
                    RechargeStatus rechargeStatus = rechargeService.findStatusByRechargeSn(sn);
                    if(null != rechargeStatus && RechargeStatus.FAILURE.equals(rechargeStatus)){
                        isCommit = true;
                    }
                }
            }
            transactionStatus = isCommit ? TransactionStatus.CommitTransaction:TransactionStatus.RollbackTransaction;
        } catch (Exception e) {
            log.error("Message Id:{}", msgId, e);
        }
        log.info("Message Id:{},transactionStatus:{}", msgId, transactionStatus.name());
        return transactionStatus;
    }
}